

#include "myloader.h"

boot_info_t boot_info;

// 这里最后一个参数 指的是 将数据读取到的 内存的位置。
static void read_disk(int sector, int sector_count, uint8_t * buf) {
    outb(0x1F6, (uint8_t) (0xE0));

	outb(0x1F2, (uint8_t) (sector_count >> 8));
    outb(0x1F3, (uint8_t) (sector >> 24));		// LBA参数的24~31位
    outb(0x1F4, (uint8_t) (0));					// LBA参数的32~39位
    outb(0x1F5, (uint8_t) (0));					// LBA参数的40~47位

    outb(0x1F2, (uint8_t) (sector_count));
	outb(0x1F3, (uint8_t) (sector));			// LBA参数的0~7位
	outb(0x1F4, (uint8_t) (sector >> 8));		// LBA参数的8~15位
	outb(0x1F5, (uint8_t) (sector >> 16));		// LBA参数的16~23位

	outb(0x1F7, (uint8_t) 0x24);

	// 这里每次读取回来的数据都是 16位的，所以指针也是16位的。
	uint16_t *data_buf = (uint16_t*) buf;
	while (sector_count-- > 0) {
		// 每次扇区读之前都要检查，等待数据就绪
		while ((inb(0x1F7) & 0x88) != 0x8) {}

		// 由于每次读取2个字节的数据，所以这里 处以2 ， 一个扇区 512 字节，需要读取 512/2 次
		for (int i = 0; i < SECTOR_SIZE / 2; i++) {
			*data_buf++ = inw(0x1F0);
		}
	}
}

static void die (int code) {
    for (;;) {
    }
}


// 这是读取 elf 文件的 代码段与数据段的函数
static uint32_t reload_elf_file (uint8_t * file_buffer) {
    // 读取的只是ELF文件，不像BIN那样可直接运行，需要从中加载出有效数据和代码
    // 简单判断是否是合法的ELF文件
    Elf32_Ehdr * elf_hdr = (Elf32_Ehdr *)file_buffer;
    if ((elf_hdr->e_ident[0] != ELF_MAGIC) || (elf_hdr->e_ident[1] != 'E')
        || (elf_hdr->e_ident[2] != 'L') || (elf_hdr->e_ident[3] != 'F')) {
        return 0;
    }

    // 然后从中加载程序头，将内容拷贝到相应的位置
	// e_phnum 代表着 program 表 的数量, 其实就一个。
	// 这里一个program 表头代表着 一个 Program 段
    for (int i = 0; i < elf_hdr->e_phnum; i++) {
		// 找到 program header 的地址。
		// e_phoof 代表着 program 段的偏移
        Elf32_Phdr * phdr = (Elf32_Phdr *)(file_buffer + elf_hdr->e_phoff) + i;
        if (phdr->p_type != PT_LOAD) {
            continue;
        }

		// 全部使用物理地址，此时分页机制还未打开
        uint8_t * src = file_buffer + phdr->p_offset;
        uint8_t * dest = (uint8_t *)phdr->p_paddr;
		//这里复制的其实 是 .text , .data .bss  段，都复制了，疑问这几个段在一个 program 段内。
        for (int j = 0; j < phdr->p_filesz; j++) {
            *dest++ = *src++;
        }

		// memsz和filesz不同时，后续要填0
		// 这是对 .bss 段的处理。
		// 按理说  p_memsz  要比 p_filesz 要大的，所以 将  p_memsz - phdr->p_filesz 大的地方写0 就可以了。
		dest= (uint8_t *)phdr->p_paddr + phdr->p_filesz;
		for (int j = 0; j < phdr->p_memsz - phdr->p_filesz; j++) {
			*dest++ = 0;
		}
    }

    return elf_hdr->e_entry;
}


//32位 C 语言执行的第一个函数
void load_kernel(void) {
    read_disk(100, 500, (uint8_t *)SYS_KERNEL_LOAD_ADDR);
	//跳转到1M 的内存处， 这里不是很理解
//	((void (*)(void))SYS_KERNEL_LOAD_ADDR)();  
	//这是在跳转到 1M 的内存地址处。  (void (*)(boot_info_t *) 这里只是在强转类型，不是很理解这里
	//((void (*)(boot_info_t *))SYS_KERNEL_LOAD_ADDR)(&boot_info);

	// 这里获得了 elf 的入口地址。
	uint32_t kernel_entry = reload_elf_file((uint8_t *)SYS_KERNEL_LOAD_ADDR);
	if (kernel_entry == 0) {
		die(-1);
	}

	((void (*)(boot_info_t *))kernel_entry)(&boot_info);


    for (;;) {}
}